/* UART asynchronous example, that uses separate RX and TX tasks

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"

#include "pan_main.h"
#include "freertos/queue.h"
#include "mqtt_client.h"
#include "cJSON.h"

static const char *TAG = "PAN_UART";
static QueueHandle_t pubilsh_queue;
uint8_t publish_data[PUBLISH_BUF];
uint8_t OTAflag;
extern esp_mqtt_client_handle_t user_client;
extern uint8_t MqttConnectFlag;
extern APP_INFO app_info;
extern char calibration_topic[90];
extern char firmware_topic[80];
extern char publish_topic[80];
extern char health_topic[90];
extern char press_topic[80];     // 压力垫数据
extern char humanbody_topic[80]; // 身高体重数据
extern char ota_progress_topic[80];
extern char config_topic[80]; // config数据
#define TXD2_PIN (GPIO_NUM_17)
#define RXD2_PIN (GPIO_NUM_16)

#define humanBody_DATA 0 //
#define publish_DATA 0
// #define humanBody_DATA    0

static void uart_rx_task()
{
    uint8_t *data = (uint8_t *)malloc(PUBLISH_BUF + 3);
    int rxBytes;
    // uint16_t len;
    while (1)
    {
        // NOTE: 时间需要注意，此处时间设置不正确会导致分包（升级设备等待时间也需要注意）
        rxBytes = uart_read_bytes(UART_NUM_2, (uint8_t *)&data[2], PUBLISH_BUF, 20 / portTICK_RATE_MS);
        if (rxBytes > 0)
        {

            data[rxBytes + 2] = 0;
            data[0] = (uint8_t)(rxBytes >> 8);
            data[1] = (uint8_t)rxBytes;
            if (data[2 + 0] == 0xa5 && data[2 + 1] == 0x5a && data[2 + 4] == 0xAA) // 进入AP
            {
                uint8_t TEMP_SerialNumber[12];
                if (MqttConnectFlag)
                {
                    esp_mqtt_client_stop(user_client);
                    esp_mqtt_client_destroy(user_client);
                    MqttConnectFlag = 0;
                }
                sendData("\xA5\x5A\x02\x00\x0B\x00", 6); // 路由器断开
                // 清空配网信息
                memcpy(TEMP_SerialNumber, app_info.SerialNumber, 12);
                memset(&app_info, 0, sizeof(app_info));
                memcpy(app_info.SerialNumber, TEMP_SerialNumber, 12);
                ESP_ERROR_CHECK(user_nvs_save_blob(NVS_APP_INFO, &app_info, sizeof(app_info)));
                ESP_LOGI(TAG, "Set Device To AP Mode!!!");
                vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s后重启
                esp_restart();
            }
            else if (data[2 + 0] == 0xa5 && data[2 + 1] == 0x5a && data[2 + 4] == 0xA7) // 信息全删
            {
                if (MqttConnectFlag)
                {
                    // esp_mqtt_client_unsubscribe(user_client, &subscribe_topic); //取消订阅
                    esp_mqtt_client_stop(user_client);
                    esp_mqtt_client_destroy(user_client);
                    MqttConnectFlag = 0;
                }
                sendData("\xA5\x5A\x02\x00\x0B\x00", 6); // 路由器断开
                memset(&app_info, 0, sizeof(app_info));  // 清空配网信息
                ESP_ERROR_CHECK(user_nvs_save_blob(NVS_APP_INFO, &app_info, sizeof(app_info)));
                ESP_LOGI(TAG, "Set Device To AP Mode!!!");
                vTaskDelay(1000 / portTICK_PERIOD_MS); // 1s后重启
                esp_restart();
            }
            else if (data[2 + 0] == 0xa5 && data[2 + 1] == 0x5a && data[2 + 4] == 0x60) // 信息全删
            {
#if humanBody_DATA
                cJSON *cjson_humanBody = NULL;
                cJSON *cjson_address = NULL;
                char *str = NULL;
                // data[2 + 5]
                /* 创建一个JSON数据对象(链表头结点) */
                cjson_humanBody = cJSON_CreateObject();
                /* 添加一条字符串类型的JSON数据(添加一个链表节点) */
                cJSON_AddStringToObject(cjson_humanBody, "type", "config");
                /* 添加一条字符串类型的JSON数据(添加一个链表节点) */
                // cJSON_AddStringToObject(cjson_humanBody, "config");
                cjson_address = cJSON_CreateObject();
                /* 添加一条整数类型的JSON数据(添加一个链表节点) */
                cJSON_AddNumberToObject(cjson_address, "hight", data[2 + 5]);
                /* 添加一条浮点类型的JSON数据(添加一个链表节点) */
                cJSON_AddNumberToObject(cjson_address, "weight", data[2 + 6]);
                // 嵌套
                cJSON_AddItemToObject(cjson_humanBody, "config", cjson_address);
                /* 打印JSON对象(整条链表)的所有数据 */
                str = cJSON_Print(cjson_humanBody);
                printf("%s\n", str);
                esp_mqtt_client_publish(user_client, (char *)&humanbody_topic, (char *)str, strlen(str), 0, 0); // 推送身高体重数据到平台
                cJSON_Delete(cjson_humanBody);
#endif
            }
            else if (data[2 + 0] == 0xa5 && data[2 + 1] == 0x5a && data[2 + 4] == 0x01) // 信息全删
            {
#if publish_DATA
                cJSON *cjson_Hdl = NULL;
                cJSON *cjson_pressureWarn = NULL;
                cJSON *cjson_bodydata = NULL;
                cJSON *cjson_warndata = NULL;
                char *str = NULL;
                uint64_t CalibrationTime;
                // 时间校准数据高低位组合
                CalibrationTime = ((uint64_t)data[2 + 5]) | ((uint64_t)data[2 + 6] << 8) |
                                  ((uint64_t)data[2 + 7] << 16) | ((uint64_t)data[2 + 8] << 24) |
                                  ((uint64_t)data[2 + 9] << 32) | ((uint64_t)data[2 + 10] << 40) |
                                  ((uint64_t)data[2 + 11] << 48) | ((uint64_t)data[2 + 12] << 56);
                // data[2 + 5]
                /* 创建一个JSON数据对象(链表头结点) */
                cjson_Hdl = cJSON_CreateObject();
                /* 添加一条字符串类型的JSON数据(添加一个链表节点) */
                cJSON_AddNumberToObject(cjson_Hdl, "time", CalibrationTime);
                /* 添加一个数组类型的JSON数据(添加一个链表节点) */
                cjson_pressureWarn = cJSON_CreateArray();
                cJSON_AddItemToArray(cjson_pressureWarn, cJSON_CreateNumber(1));
                cJSON_AddItemToArray(cjson_pressureWarn, cJSON_CreateNumber(1));
                cJSON_AddItemToArray(cjson_pressureWarn, cJSON_CreateNumber(1));
                cJSON_AddItemToArray(cjson_pressureWarn, cJSON_CreateNumber(1));
                cJSON_AddItemToObject(cjson_Hdl, "overpressureWarn", cjson_pressureWarn);
                /* 添加一个数组类型的JSON数据(添加一个链表节点) */
                cjson_bodydata = cJSON_CreateArray();
                cJSON_AddItemToArray(cjson_bodydata, cJSON_CreateNumber(100));
                cJSON_AddItemToArray(cjson_bodydata, cJSON_CreateNumber(100));
                cJSON_AddItemToObject(cjson_Hdl, "bodydata", cjson_bodydata);
                cJSON_AddNumberToObject(cjson_Hdl, "mode", data[2 + 16]);              // 模式 0 静态 1护理  2坐姿  16 波动
                cJSON_AddNumberToObject(cjson_Hdl, "postureInterval", data[2 + 17]);   // 设置交替模式  单位  分钟
                cJSON_AddNumberToObject(cjson_Hdl, "turnoverMode", data[2 + 18]);      // 翻身方式 0 不翻 1 交替  2 左翻 3右翻
                cJSON_AddNumberToObject(cjson_Hdl, "turnoverInterval", data[2 + 19]);  // 设置翻身交替时间  单位 分钟
                cJSON_AddNumberToObject(cjson_Hdl, "postureRemaining", data[2 + 20]);  // 剩余交替时间 单位 秒
                cJSON_AddNumberToObject(cjson_Hdl, "turnoverRemaining", data[2 + 21]); // 剩余翻身交替时间 单位 秒
                cJSON_AddNumberToObject(cjson_Hdl, "currentAirColumn", data[2 + 22]);  // 当前充气气柱 1 A 2 B 3 AB
                cJSON_AddNumberToObject(cjson_Hdl, "pressure", data[2 + 23]);          // 工作气柱气压
                cJSON_AddNumberToObject(cjson_Hdl, "occupancy", data[2 + 24]);         // 当前充气气柱 1 A 2 B 3 AB
                cJSON_AddNumberToObject(cjson_Hdl, "bodyMove", data[2 + 25]);          // 工作气柱气压
                /* 添加一个数组类型的JSON数据(添加一个链表节点) */
                cjson_warndata = cJSON_CreateArray();
                cJSON_AddItemToArray(cjson_warndata, cJSON_CreateNumber(1));
                cJSON_AddItemToArray(cjson_warndata, cJSON_CreateNumber(0));
                cJSON_AddItemToArray(cjson_warndata, cJSON_CreateNumber(1));
                cJSON_AddItemToObject(cjson_Hdl, "warnHdl", cjson_warndata);

                /* 添加一个数组类型的JSON数据(添加一个链表节点) */
                // cjson_heightWeight = cJSON_CreateArray();

                /* 添加一条字符串类型的JSON数据(添加一个链表节点) */
                // cJSON_AddNumberToObject(cjson_Hdl, "time", CalibrationTime);

                /* 打印JSON对象(整条链表)的所有数据 */
                str = cJSON_Print(cjson_Hdl);
                printf("%s\n", str);
                esp_mqtt_client_publish(user_client, (char *)&publish_topic, (char *)str, strlen(str), 0, 0); // 推送身高体重数据到平台
                cJSON_Delete(cjson_Hdl);
#endif
            }

            ESP_LOGI(TAG, "Read %d bytes: %s", rxBytes, data);
            // ESP_LOG_BUFFER_HEXDUMP(TAG, data, rxBytes, ESP_LOG_INFO);
            if (MqttConnectFlag)
            {
                // esp_mqtt_client_publish(user_client, &publish_topic,(char*) data, rxBytes, 0, 0);
                if (data[2 + 4] == 0x10)
                    ESP_LOGI(TAG, "receive firmware");
                else if (data[2 + 4] == 0x20)
                    ESP_LOGI(TAG, "receive calibration");
                else
                    ESP_LOGI(TAG, "receive data");
                xQueueSend(pubilsh_queue, data, 20 / portTICK_RATE_MS);
                ESP_LOGI(TAG, "sent controlbox data to app: %.*s", rxBytes, data);
            }
        }
    }
    free(data);
}

static void uart_pubilsh_task(void *pvParameters)
{
    uint16_t len;
    uint8_t FrameType;
    while (1)
    {
        if (xQueueReceive(pubilsh_queue, &publish_data, 20 / portTICK_RATE_MS))
        {
            len = (uint16_t)(publish_data[0] << 8) | (uint16_t)publish_data[1];
            FrameType = publish_data[2 + 4];
            if (FrameType == 0x20) // 时间校准主题
            {
                ESP_LOGI(TAG, "enter calibration pubilsh queue!");
                esp_mqtt_client_publish(user_client, (char *)&calibration_topic, (char *)&publish_data[2], len, 0, 0);
            }
            else if (FrameType == 0x10) // 固件主题
            {
                //  ESP_LOGI(TAG, "enter firmware pubilsh queue!");
                //  esp_mqtt_client_publish(user_client, (char *)&firmware_topic, (char *)&publish_data[2], len, 0, 0);
                ESP_LOGI(TAG, "GO INTO OTA %02x,%02x,%02x,%02x,%02x",
                         publish_data[2], publish_data[3], publish_data[4], publish_data[5], publish_data[6]);
                switch (publish_data[2 + 5]) // 0:等待（等待回传）;1：OK;2:重传
                {
                case 1:
                    OTAflag = 1;
                    break;
                case 2:
                    OTAflag = 2;
                    break;
                case 3:
                    OTAflag = 3;
                    break;
                }
            }
            else if (FrameType == 0x30) // 下位机信息
            {
                if (publish_data[2 + 5] == 0x01)
                {
                    app_info.mattress_version[0] = publish_data[2 + 6];
                    app_info.mattress_version[1] = publish_data[2 + 7];
                    app_info.mattress_version[2] = publish_data[2 + 8];
                    app_info.mattress_version[3] = publish_data[2 + 9];
                }
            }
            else if (FrameType == 0x40)
            {
                esp_mqtt_client_publish(user_client, (char *)&health_topic, (char *)&publish_data[2 + 5], len - 5, 0, 0);
                ESP_LOGI(TAG, "health topic:%s", &publish_data[2 + 5]);
            }
            else if (FrameType == 0x50)
            {
                esp_mqtt_client_publish(user_client, (char *)&press_topic, (char *)&publish_data[2], len, 0, 0); // 推送压力垫数据到平台
                for (uint16_t i = 0; i < 10; i++)                                                                // 1520
                {
                    printf(" %x ", publish_data[i + 2]);
                    // ESP_LOGI(TAG, "press topic:%s", &publish_data[i + 5]);
                }
                // ESP_LOGI(TAG, "health topic:%s", &publish_data[2 + 5]);
            }
            else if (FrameType == 0x60)
            {
                esp_mqtt_client_publish(user_client, (char *)&humanbody_topic, (char *)&publish_data[2 + 5], len, 0, 0); // 推送身高体重数据到平台
                // for (uint16_t i = 0; i < 7; i++)
                // {
                // ESP_LOGI(TAG, " %x ",publish_data[i + 2]);
                //  printf(" %x ", publish_data[i + 2]);
                // ESP_LOGI(TAG, " %x ", len);
                //}
            }
            else
            {
                ESP_LOGI(TAG, "enter data pubilsh queue!");
                // for (uint16_t i = 0; i < 38; i++)
                // {
                //     printf(" %x ", publish_data[i + 2]);
                // }
                ESP_LOGI(TAG, "Length %d ", len);
                //if (publish_data[2 + 0] == 0xa5 && publish_data[2 + 1] == 0x5a)
               // {
                    esp_mqtt_client_publish(user_client, (char *)&publish_topic, (char *)&publish_data[2], len, 0, 0);
                //}
                    
            }
            memset(publish_data, 0, sizeof(publish_data));
        }
    }
}

int sendData(const char *data, const int len)
{
    const int txBytes = uart_write_bytes(UART_NUM_2, data, len);
    ESP_LOGI(TAG, "send %d bytes to cb: %.*s\r\n", txBytes, txBytes, data);
    return txBytes;
}

void uart_init(void)
{
    const uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_APB,
    };
    // We won't use a buffer for sending data.
    uart_driver_install(UART_NUM_2, PUBLISH_BUF * 2, 0, 0, NULL, 0);
    uart_param_config(UART_NUM_2, &uart_config);
    uart_set_pin(UART_NUM_2, TXD2_PIN, RXD2_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);

    pubilsh_queue = xQueueCreate(3, sizeof(publish_data));
    xTaskCreate(uart_rx_task, "uart_rx_task", 4096, NULL, 12, NULL);
    xTaskCreate(uart_pubilsh_task, "uart_pubilsh_task", PUBLISH_BUF * 5, NULL, configMAX_PRIORITIES - 1, NULL);
}
